#include "LabyrinthGame.h"
#include "UserInterface.h"
#include "Commands.h"

#include <glog/logging.h>

LabyrinthGame::LabyrinthGame():
	moves(0),
	currentPosition(Position::center(Labyrinth::WIDTH, Labyrinth::HEIGHT)) {
}

LabyrinthGame::LabyrinthGame(const Labyrinth & labyrinth) :
	labyrinth(labyrinth),
	moves(0),
	currentPosition(Position::center(Labyrinth::WIDTH, Labyrinth::HEIGHT)) {
}

void LabyrinthGame::startNewGame() {
	this->labyrinth.rebuild();
	UserInterface::out << "Welcome to 'Labyrinth' game. Please try to escape. Use 'top' to view the top scoreboard, "
		"'restart' to start a new game and 'exit' to quit the game." << endl;
	this->moves = 0;
	this->currentPosition = Position::center(Labyrinth::WIDTH, Labyrinth::HEIGHT);
	try {
		this->labyrinth.setCell(this->currentPosition, Cell::PLAYER);
	}
	catch (const invalid_argument & invalidArgumentException) {
		DLOG(FATAL) << "Program exited with message: " << invalidArgumentException.what();
	}
	catch (const out_of_range & outOfRangeException) {
		DLOG(FATAL) << "Program exited with message: " << outOfRangeException.what();
	}
	UserInterface::out << this->labyrinth;
}

void LabyrinthGame::addScoreToScoreboard() {
	try {
		if(this->scoreboard.isTopScore(this->moves)){
			string playerName("");
			while(! UserInterface::validName(playerName)){
				playerName = UserInterface::promptUser("Please enter your name for the top scoreboard: ");
			}
			this->scoreboard.addScore(playerName, this->moves);
		}
	}
	catch (const invalid_argument & invalidArgumentException) {
		DLOG(FATAL) << "Program exited with message: " << invalidArgumentException.what();
	}
}

void LabyrinthGame::move(const Position & newPosition) {
	try {
		if (this->labyrinth.getCell(newPosition) == Cell::OCCUPIED) {
			UserInterface::out << "Invalid move!" << endl;
			return;
		}
		this->moves++;
		if (this->labyrinth.isOnEdge(newPosition)) {
			UserInterface::out << "Congratulations! You escaped in " << this->moves << " moves." << endl;
			addScoreToScoreboard();
			UserInterface::out << this->scoreboard;
			this->startNewGame();
			return;
		}
		this->labyrinth.setCell(this->currentPosition, Cell::FREE);
		this->labyrinth.setCell(newPosition, Cell::PLAYER);
		this->currentPosition = newPosition;
		UserInterface::out << this->labyrinth;
	}
	catch (const invalid_argument & invalidArgumentException) {
		DLOG(FATAL) << "Program exited with message: " << invalidArgumentException.what();
	}
	catch (const out_of_range & outOfRangeException) {
		DLOG(FATAL) << "Program exited with message: " << outOfRangeException.what();
	}
}

void LabyrinthGame::play() {
	this->startNewGame();
	Command command(COMMAND_INVALID_COMMAND);
	while (command != COMMAND_EXIT) {
		string commandName = UserInterface::promptUser("Enter your move (L=left, R-right, U=up, D=down): ");
		command = Commands::parse(commandName);
		switch (command) {
			case COMMAND_RESTART:
				this->startNewGame();
				break;
			case COMMAND_TOP:
				UserInterface::out << scoreboard;
				break;
			case COMMAND_MOVE_LEFT:
				this->move(this->currentPosition.left());
				break;
			case COMMAND_MOVE_UP:
				this->move(this->currentPosition.up());
				break;
			case COMMAND_MOVE_RIGHT:
				this->move(this->currentPosition.right());
				break;
			case COMMAND_MOVE_DOWN:
				this->move(this->currentPosition.down());
				break;
			case COMMAND_INVALID_COMMAND:
				UserInterface::out << "Invalid command!" << endl;
				break;
			case COMMAND_EXIT:
				break;
			default:
				DLOG(FATAL) << "Found unknown command."; //must not happen
				break;
		}
	}
	UserInterface::out << "Good Bye!" << endl;
}

